Day27 Ruby的Hash預設值 How to set default value in hash?
在第14天的時候,我們曾經提到Hash(雜湊)
是一對key(鍵)
與value(值)
的集合。今天的大哉問是,我們要如何設定預設值呢?
沒錯!起手式就是來翻閱Ruby API手冊看看裡面的例子,並依照我自己的喜好,改寫成容易被我記憶學習的變數。
記得我在第14天使用過銀行帳戶的例子,在這裡繼續沿用類似的情境。假設我創建一個hash記錄我的銀行帳戶名稱和存款金額:
hbank = Hash.new("Bank Name")
hbank["CAN"] = 100
hbank["NAB"] = 200
p hbank.keys #=> 銀行名["CAN", "NAB"]
p hbank.values #=> 存款金額[100, 200]
p hbank["CAN"] #=> 100
p hbank["NAB"] #=> 200
如果我要查閱某個並沒有在此Hash的銀行名稱呢?
p hbank["WEST"] #=> "Bank Name"
它會出現預設值`Bank Name。
如果你以為這題面試題這麼簡單,那就大錯特錯了!(不要來~污辱我的美)我們必須深入探討另一個常見實務場景:如何改動Hash裡的值呢?
你或許會想到:使用method
!趕快搬出常見的字串方法使一使:
p hbank["WEST"] #=> "Bank Name"
p hbank["WEST"].upcase! #=> "BANK NAME"
p hbank["WEST"] #=> "BANK NAME"
第14天講過:
加上!驚嘆號的方法,通常代表小心!注意!的意思。
Array#.map!
方法會讓原本的物件陣列值被改變!
因此這裡的 string#.upcase!
就讓Hash裡的Bank Name
硬生生地變成BANK NAME
了。因此,我們了解到陣列
是個可變的
物件。(Array in Ruby is mutable object)
要預設Hash,我們還必須複習第11天的block(區塊)
,因為Hash和block有密不可分的關係!
hbank_key = Hash.new { |hash,name| hash[name] = "Bank Name: #{name}" }
p hbank_key["ANZ"] #=> "Bank Name: ANZ"
p hbank_key["ANZ"].upcase! #=> "BANK NAME: ANZ"
p hbank_key["ANZ"]
#=> "BANK NAME: ANZ" 變大寫了
p hbank_key["CBA"] #=> "Bank Name: CBA" 還是小寫
p hbank_key.keys #=> ["ANZ", "CBA"]
hbank_key
這個雜湊的例子比第一個版本hbank
稍微進步,讓hbank_key["CBA"]
的‘值並不會因為hbank_key["ANZ"].upcase!
作怪,而受到影響。
Stackovrflow 原文這句話解釋的很清楚:
It is the block’s responsibility to store the value in the hash if required.
<<
與 +=
結合比較如果我們硬要唱反調,讓Hash設定default value時不使用block,看會發生什麼情況?
我們在第17天已了解到:<<
會改變變數及常數,因此在這裡hsh_array[:z]
被指定了新值。
hsh_array = Hash.new([])
p hsh_array # => {} 預設是空hash
hsh_array[:a] << 'This is symbol a'
hsh_array[:b] << 'This is symblo b'
p hsh_array[:z]
# => ["This is symbol a", "This is symblo b"]
p hsh_array # => {} hash不變,仍是空hash
例子1 在Hash
內使用<<
指定值時(the assignment way),hsh_array[:z]
原本預設為[],卻受到[:a]與[:b]的改變而影響。那現在改成<<
(the mutable way)呢?
hsh_block = Hash.new { |h, k| h[k] = [] }
p hsh_block[:a] << 'This is symbol a' #=> ["a"]
p hsh_block[:b] << 'This is symblo b' #=> ["b"]
p hsh_block[:z] #[]
p hsh_block #=> {:a=>["This is symbol a"], :b=>["This is symblo b"], :z=>[]}
hsh_block[:z]
仍與預設值相同,這次換成整個Hashhsh_block
改變。
關於串接字串,來複習一下第8天的+=
是mutable
的方法,其object_id
會隨著新串接上的字串,而改變記憶體位置。
hsh_plus = Hash.new([])
p hsh_plus # => {} 預設是空hash
p hsh_plus[:a] += ['This is symbol a']
p hsh_plus[:b] += ['This is symblo b']
p hsh_plus[:z]
# => [] 空array
p hsh_plus
# {:a=>["This is symbol a"], :b=>["This is symblo b"]}
# hash is mutable< 預設值已被改變
freeze?
固定值最後,如果要讓hash的+=
不透過block的作用也能變成imutable
,我們就要來複習Day17的freeze?
方法:
hsh_freeze = Hash.new([].freeze)
p hsh_freeze[:a] += ['This is symbol a'] #=> ["This is symbol a"]
p hsh_freeze[:b] += ['This is symbol b'] #=> ["This is symbol b"]
p hsh_freeze[:z] #=> [] #=> hash值不受更動,仍為空
p hsh_freeze #=> {:a=>["This is symbol a"], :b=>["This is symbol b"]}
結論:
看完幾篇文章,發現有寫工程師偏好不要變動預設變數、但也有些人偏好能夠更彈性的變動變數,端看個人開發習慣。不過寫完落落長的比較,這個結論你一定要熟記喔!
We must store the default value in the
hash
from within theblock
if we wish to use<<
instead of<<=
.
Ref: